package com.tau.iplayer.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.tau.iplayer.client.DataStoreService;
import com.tau.iplayer.client.exceptions.NotLoggedInException;
import com.tau.iplayer.client.models.Playlist;
import com.tau.iplayer.client.models.Track;
import com.tau.iplayer.client.models.TrackTransaction;

@SuppressWarnings("serial")
public class DataStoreServiceImpl extends RemoteServiceServlet implements
		DataStoreService {

	private static final Logger LOG = Logger
			.getLogger(DataStoreServiceImpl.class.getName());
	private static final PersistenceManagerFactory PMF = JDOHelper
			.getPersistenceManagerFactory("transactions-optional");

	@Override
	public void addTrack(String userID, Track track, Playlist playlist)
			throws NotLoggedInException {
		checkLoggedIn(userID);
		PersistenceManager pm = getPersistenceManager();
		TrackData trackData = getTrackData(userID, track, playlist);
		try {
			pm.makePersistent(trackData);
		} finally {
			pm.close();
		}
	}

	private TrackData getTrackData(String userID, Track track, Playlist playlist) {
		return new TrackData(userID, track.getTitle(), track.getArtist(), track
				.getDuration(), playlist.getName(), track.getVideoID(), track
				.getThumbnailURL(), (int) track.getRating());
	}

	private void checkLoggedIn(String userID) throws NotLoggedInException {
		if (userID == null || userID == "-1") {
			throw new NotLoggedInException("Not logged in.");
		}
	}

	private PersistenceManager getPersistenceManager() {
		return PMF.getPersistenceManager();
	}

	@Override
	public Map<String, Playlist> getPlaylists(String userID)
			throws NotLoggedInException {
		checkLoggedIn(userID);
		PersistenceManager pm = getPersistenceManager();
		Map<String, Playlist> playlists = new TreeMap<String, Playlist>();
		try {
			Query q = pm.newQuery(PlaylistData.class, "userID == u");
			q.declareParameters("String u");
			List<PlaylistData> playlistDatas = (List<PlaylistData>) q
					.execute(userID);
			for (PlaylistData playlistData : playlistDatas) {
				playlists.put(playlistData.getName(),
						translatePlaylistData(playlistData));
			}
		} finally {
			pm.close();
		}
		return playlists;
	}

	/**
	 * converts playlistData to playlist
	 * 
	 * @param playlistData
	 *            the data to convert
	 * @return the converted playlist
	 */
	private Playlist translatePlaylistData(PlaylistData playlistData) {
		return new Playlist(playlistData.getId(), playlistData.getName());
	}

	@Override
	public void addPlaylist(String userID, Playlist playlist)
			throws NotLoggedInException {
		checkLoggedIn(userID);
		PersistenceManager pm = getPersistenceManager();
		PlaylistData playlistData = getPlaylistData(userID, playlist);
		try {
			pm.makePersistent(playlistData);
		} finally {
			pm.close();
		}
	}

	private PlaylistData getPlaylistData(String userID, Playlist playlist) {
		PlaylistData playlistData = new PlaylistData(userID, playlist.getName());
		return playlistData;
	}

	@Override
	public List<Track> getTracks(String userID, Playlist playlist)
			throws NotLoggedInException {
		checkLoggedIn(userID);
		PersistenceManager pm = getPersistenceManager();
		List<Track> tracks = new ArrayList<Track>();
		try {
			Query q = pm.newQuery(TrackData.class,
					"userID == u && playlistName == playlistParam");
			q.declareParameters("String u, String playlistParam");
			List<TrackData> trackDatas = (List<TrackData>) q.execute(userID,
					playlist.getName());
			for (TrackData trackData : trackDatas) {
				tracks.add(translateTrackData(trackData));
			}
		} finally {
			pm.close();
		}
		return tracks;
	}

	/**
	 * converts trackData to track
	 * 
	 * @param trackData
	 *            the data to convert
	 * @return the converted track
	 */
	private Track translateTrackData(TrackData trackData) {
		return new Track(trackData.getId(), trackData.getTitle(), trackData
				.getArtist(), trackData.getDuration(), trackData.getVideoID(),
				trackData.getThumbnailURL(), trackData.getRating());
	}

	@Override
	public void deleteTrack(String userID, Track track)
			throws NotLoggedInException {
		checkLoggedIn(userID);
		PersistenceManager pm = getPersistenceManager();
		try {
			TrackData trackData = pm.getObjectById(TrackData.class, track
					.getId());
			pm.deletePersistent(trackData);
		} finally {
			pm.close();
		}
	}

	@Override
	public void updateTrack(String userID, Track newTrack)
			throws NotLoggedInException {
		checkLoggedIn(userID);
		PersistenceManager pm = getPersistenceManager();
		try {
			TrackData trackData = pm.getObjectById(TrackData.class, newTrack
					.getId());
			trackData.setArtist(newTrack.getArtist());
			trackData.setTitle(newTrack.getTitle());
			trackData.setRating((int) newTrack.getRating());
		} finally {
			pm.close();
		}
	}

	@Override
	public void moveTrack(String userID, Track track, Playlist newPlaylist)
			throws NotLoggedInException {
		checkLoggedIn(userID);
		PersistenceManager pm = getPersistenceManager();
		try {
			TrackData trackData = pm.getObjectById(TrackData.class, track
					.getId());
			trackData.setPlaylistName(newPlaylist.getName());
		} finally {
			pm.close();
		}
	}

	@Override
	public void renamePlaylist(String userID, Playlist playlist, String name)
			throws NotLoggedInException {
		checkLoggedIn(userID);
		PersistenceManager pm = getPersistenceManager();
		try {
			Query q = pm.newQuery(TrackData.class,
					"userID == u && playlistName == playlistParam");
			q.declareParameters("String u, String playlistParam");
			List<TrackData> trackDatas = (List<TrackData>) q.execute(userID,
					playlist.getName());
			for (TrackData trackData : trackDatas) {
				trackData.setPlaylistName(name);
			}
			PlaylistData playlistData = pm.getObjectById(PlaylistData.class,
					playlist.getId());
			playlistData.setName(name);
		} finally {
			pm.close();
		}
	}

	@Override
	public void deletePlaylist(String userID, Playlist playlist)
			throws NotLoggedInException {
		checkLoggedIn(userID);
		PersistenceManager pm = getPersistenceManager();
		try {
			Query q = pm.newQuery(TrackData.class,
					"userID == u && playlistName == playlistParam");
			q.declareParameters("String u, String playlistParam");
			List<TrackData> trackDatas = (List<TrackData>) q.execute(userID,
					playlist.getName());
			pm.deletePersistentAll(trackDatas);
			PlaylistData playlistData = pm.getObjectById(PlaylistData.class,
					playlist.getId());
			pm.deletePersistent(playlistData);
		} finally {
			pm.close();
		}
	}

	@Override
	protected String readContent(HttpServletRequest request)
			throws ServletException, IOException {
		return RPCServletUtils.readContentAsUtf8(request, false);
	}

	@Override
	public List<Track> getTracks(List<String> userIDs)
			throws NotLoggedInException {
		List<Track> tracks = new ArrayList<Track>();
		String viewerID = userIDs.get(0);
		checkLoggedIn(viewerID);
		PersistenceManager pm = getPersistenceManager();
		String queryString = "rating > 0 && (userID == u0";
		String paramsString = "String u0";
		for (int i = 1; i < userIDs.size(); i++) {
			queryString += (" || userID == u" + i);
			paramsString += (", String u" + i);
		}
		queryString += ")";
		try {
			Query q = pm.newQuery(TrackData.class, queryString);
			q.declareParameters(paramsString);
			List<TrackData> trackDatas = (List<TrackData>) q
					.executeWithArray(userIDs.toArray());
			for (TrackData trackData : trackDatas) {
				tracks.add(translateTrackData(trackData));
			}
		} finally {
			pm.close();
		}
		return tracks;
	}

	@Override
	public List<Track> getAllTracks() {
		List<Track> tracks = new ArrayList<Track>();
		PersistenceManager pm = getPersistenceManager();
		try {
			List<TrackData> trackDatas = (List<TrackData>) pm.newQuery(
					TrackData.class, "rating > 0").execute();
			for (TrackData trackData : trackDatas) {
				tracks.add(translateTrackData(trackData));
			}
		} finally {
			pm.close();
		}
		return tracks;
	}

	@Override
	public void storeTrackTransaction(TrackTransaction transaction)
			throws NotLoggedInException {
		checkLoggedIn(transaction.getSenderID());
		PersistenceManager pm = getPersistenceManager();
		TrackTransactionData transactionData = toTrackTransactionData(transaction);
		try {
			pm.makePersistent(transactionData);
		} finally {
			pm.close();
		}
	}

	private TrackTransactionData toTrackTransactionData(
			TrackTransaction transaction) {
		TrackTransactionData result = new TrackTransactionData();
		result.setDate(transaction.getDate());
		result.setMessage(transaction.getMessage());
		result.setRead(transaction.isRead());
		result.setReceiverID(transaction.getReceiverID());
		result.setSenderID(transaction.getSenderID());
		result.setThumbnailURL(transaction.getThumbnailURL());
		result.setVideoID(transaction.getVideoID());
		result.setArtist(transaction.getArtist());
		result.setTitle(transaction.getTitle());
		result.setDuration(transaction.getDuration());
		return result;
	}

	private TrackTransaction toTrackTransaction(
			TrackTransactionData transactionData) {
		TrackTransaction result = new TrackTransaction();
		result.setArtist(transactionData.getArtist());
		result.setDate(transactionData.getDate());
		result.setId(transactionData.getId());
		result.setMessage(transactionData.getMessage());
		result.setRead(transactionData.isRead());
		result.setReceiverID(transactionData.getReceiverID());
		result.setSenderID(transactionData.getSenderID());
		result.setThumbnailURL(transactionData.getThumbnailURL());
		result.setTitle(transactionData.getTitle());
		result.setVideoID(transactionData.getVideoID());
		result.setDuration(transactionData.getDuration());
		return result;
	}

	@Override
	public List<TrackTransaction> getTrackTransactions(String receiverID)
			throws NotLoggedInException {
		checkLoggedIn(receiverID);
		PersistenceManager pm = getPersistenceManager();
		List<TrackTransaction> transactions = new ArrayList<TrackTransaction>();
		try {
			Query q = pm
					.newQuery(TrackTransactionData.class, "receiverID == u");
			q.declareParameters("String u");
			q.setOrdering("date desc");
			List<TrackTransactionData> transactionDatas = (List<TrackTransactionData>) q
					.execute(receiverID);
			for (TrackTransactionData transactionData : transactionDatas) {
				transactions.add(toTrackTransaction(transactionData));
			}
		} finally {
			pm.close();
		}
		return transactions;
	}

	@Override
	public void deleteTrackTransaction(String userID,
			TrackTransaction transaction) throws NotLoggedInException {
		checkLoggedIn(userID);
		PersistenceManager pm = getPersistenceManager();
		try {
			TrackTransactionData transactionData = pm.getObjectById(
					TrackTransactionData.class, transaction.getId());
			pm.deletePersistent(transactionData);
		} finally {
			pm.close();
		}
	}
}
